// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_OZONE_PLATFORM_FLATLAND_TESTS_FAKE_FLATLAND_H_
#define UI_OZONE_PLATFORM_FLATLAND_TESTS_FAKE_FLATLAND_H_

#include <fuchsia/scenic/scheduling/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl_test_base.h>
#include <fuchsia/ui/pointer/cpp/fidl.h>
#include <lib/async/dispatcher.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/fidl/cpp/interface_request.h>

#include <string>

#include "base/callback.h"

namespace ui {

// A lightweight fake implementation of the scenic Flatland API.  The fake has
// no side effects besides mutating its own internal state.
//
// The fake allows tests to do a few things that would be difficult using either
// a mock implementation or the real implementation:
//   + It allows the user to hook `Present` invocations and respond with
//   stubbed-out `FuturePresentationTimes`, but more crucially it mimics the
//   real scenic behavior of only processing commands when a `Present` is
//   invoked.
//   TODO(fxb/85619): Implement the following bullet-points
//   + It allows the user to inspect a snapshot of the scene graph at any moment
//   in time, via the `SceneGraph()` accessor.
//   + It stores the various flatland resources generated by commands into a
//   std::unordered_map, and also correctly manages the resource lifetimes via
//   reference counting. This allows a resource to stay alive if its parent
//   still holds a reference to it, in the same way the real scenic
//   implementation would.
//   + The resources returned by `SceneGraph()` that the test uses for
//   inspection are decoupled from the resources managed internally by the
//   `FakeFlatland` itself -- they are a snapshot of the scene graph at that
//   moment in time, with all snapshot state being cloned from the underlying
//   scene graph state.  This allows the `FakeFlatland` and test to naturally
//   use `shared_ptr` for reference counting and mimic the real scenic behavior
//   exactly, instead of an awkward index-based API.
//
// Error handling / flatland disconnection is still WIP.  FakeFlatland will
// likely generate a CHECK in any place where the real scenic would disconnect
// the flatland or send a FlatlandError.
//
// Input is not handled.
//
// Rendering is not handled.
//
// Cross-flatland links between View and Viewport are not handled.
class FakeFlatland
    : public fuchsia::ui::composition::testing::Flatland_TestBase {
 public:
  using PresentHandler =
      base::RepeatingCallback<void(fuchsia::ui::composition::PresentArgs)>;
  using ViewRefFocusedRequestHandler =
      fidl::InterfaceRequestHandler<fuchsia::ui::views::ViewRefFocused>;
  using TouchSourceRequestHandler =
      fidl::InterfaceRequestHandler<fuchsia::ui::pointer::TouchSource>;

  FakeFlatland();
  ~FakeFlatland() override;

  bool is_bound() const { return binding_.is_bound(); }

  const std::string& debug_name() const { return debug_name_; }

  // Bind this session's FIDL channels to the `dispatcher` and allow processing
  // of incoming FIDL requests.
  //
  // This can only be called once.
  fidl::InterfaceHandle<fuchsia::ui::composition::Flatland> Connect(
      async_dispatcher_t* dispatcher = nullptr);

  // Returns a request handler that binds the incoming FIDL requests to this
  // session's FIDL channels on the `dispatcher`.
  //
  // This can only be called once.
  fidl::InterfaceRequestHandler<fuchsia::ui::composition::Flatland>
  GetRequestHandler(async_dispatcher_t* dispatcher = nullptr);

  // Disconnect the session's FIDL channels with an error.
  // TODO: Call this internally upon command error, instead of CHECK'ing.
  void Disconnect(fuchsia::ui::composition::FlatlandError error);

  // Set a handler for `Present`-related FIDL calls' return values.
  void SetPresentHandler(PresentHandler present_handler);

  // Fire an `OnNextFrameBegin` event.  Call this first after a `Present` in
  // order to give additional present tokens to the client and simulate scenic's
  // normal event flow.
  void FireOnNextFrameBeginEvent(
      fuchsia::ui::composition::OnNextFrameBeginValues
          on_next_frame_begin_values);

  // Fire an `OnFramePresented` event.  Call this second after a `Present` in
  // order to inform the client of returned frames and simulate scenic's normal
  // event flow.
  void FireOnFramePresentedEvent(
      fuchsia::scenic::scheduling::FramePresentedInfo frame_presented_info);

  void SetViewRefFocusedRequestHandler(ViewRefFocusedRequestHandler handler);
  void SetTouchSourceRequestHandler(TouchSourceRequestHandler handler);

 private:
  // |fuchsia::ui::composition::testing::Flatland_TestBase|
  void NotImplemented_(const std::string& name) override;

  // |fuchsia::ui::composition::Flatland|
  void Present(fuchsia::ui::composition::PresentArgs args) override;

  // |fuchsia::ui::composition::Flatland|
  void CreateView2(
      fuchsia::ui::views::ViewCreationToken token,
      fuchsia::ui::views::ViewIdentityOnCreation view_identity,
      fuchsia::ui::composition::ViewBoundProtocols view_protocols,
      fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher>
          parent_viewport_watcher) override;

  // |fuchsia::ui::composition::Flatland|
  void SetDebugName(std::string debug_name) override;

  fidl::Binding<fuchsia::ui::composition::Flatland> binding_;

  std::string debug_name_;

  PresentHandler present_handler_;
  ViewRefFocusedRequestHandler view_ref_focused_handler_;
  TouchSourceRequestHandler touch_source_request_handler_;
};

}  // namespace ui

#endif  // UI_OZONE_PLATFORM_FLATLAND_TESTS_FAKE_FLATLAND_H_
